#ifndef __tenacitas_interlace_text_core__layouts__h__
#define __tenacitas_interlace_text_core__layouts__h__

/** @file */


/** @todo */

// ==> C++ Headers
#include <vector>
#include <algorithm>

// ==> 3rds Headers


// ==> Our Headers
#include <tenacitas.return_code.core/rc.h>
#include "layout.h"

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace interlace_text {
        /** ==> bussiness, core, displaying, interacting, transmitting,
            storing */
        namespace core {

            // ==> Typedefs

            // ==> Pre-Declarations

            // ==> Attributes 

            // ==> Functions

            // ==> Classes

            /**
               @tparam supplier must define @p text and @p interlace

               @tparam supplier::text must define @p text, which is the type of
               string to be used: @p std::string, or @p std::wstring, or @p
               std::u16string, or @p std::u32string.

               @tparam supplier::interlace is the algorithm used to interlace the
               texts 

               @nosubgrouping
            */
            template <typename supplier>
            class layouts {
            public:

                // ==> layouts - Public Internal Types

                /** */
                typedef typename supplier::text text;

                /** */
                typedef typename core::layout<supplier> layout;

                /** */
                typedef typename layout::layout_id layout_id;

                /** */
                typedef typename layout::orientation orientation;
                
                /** */
                typedef typename std::vector<layout> collection;

                /** */
                typedef typename collection::size_type layout_index;

                /** */
                typedef core::coordinate<supplier> coordinate;

                /** */
                typedef typename coordinate::position position;

                /** */
                typedef core::coordinates<supplier> coordinates;

                /** */
                typedef typename coordinates::index coordinate_index;
                

                /** */
                enum constants : constant_id {index_overflow = 0,
                        index_underflow = 1};

                // ==> layouts - Friends

                /** */
                friend class  supplier::interlace;

                /** Class allowed to access private members of @p layouts, in
                    order to perform tests */
                friend class layouts_tester;

                /** */
                friend std::ostream & operator << (std::ostream & p_out,
                                                   const layouts & p_coo) {

                    for (size_t _i = 0; _i < p_coo.m_collection.size(); ++_i){
                        p_out << p_coo.m_collection[_i] << " ";
                    }
                    return p_out;                
                }                

                // ==> layouts - Public Constructors

                /** */
                inline layouts();

                /** */
                inline layouts(const layouts &);

                /** */
                inline layouts(layouts &&);

                /** Destructor */
                inline ~layouts();

                // ==> layouts - Public Accessors

                /** */
                inline layout_index get_size() const;
                
                /** */
                inline bool is_positioned() const noexcept;

                /** returns get_size() - 1 */
                inline layout_index get_last();
                
                /** */
                void add(const text &);

                /** the gratest line and column */
                coordinate greatest() const;

                // ==> layouts - Public Helpers

                template <typename t_out>
                void print(t_out & p_out);
                
                // ==> layouts - Public Operators

                /** */
                inline layouts & operator=(const layouts &) ;

                /** */
                inline layouts & operator=(layouts &&) ;

                /** */
                inline bool operator ==(const layouts & ) noexcept;

                /** */
                inline bool operator !=(const layouts & ) noexcept;

                /**  
                     @throw rc<>
                */
                inline const layout & operator[](layout_index p_index) const;


                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void operator delete (void *p_class_id) = delete;

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                // ==> layouts - Public Attributes

                static const class_id id =   14311146433408;

            private:

                // ==> layouts - Private Internal Types

                
                struct compare_layout {
                    compare_layout(const text & p_text)
                        : m_text(p_text) {}
                    
                    bool operator()(const layout & p_layout) {
                        return (p_layout.get_text() == m_text);
                    }
                    
                private:
                    text m_text;
                };

                /** */
                struct compare_layout_by_text_size_descendant {
                    inline bool
                    operator()(const layout & p_l1, const layout & p_l2) {
                        return (p_l1.get_text().size() >
                                p_l2.get_text().size());
                    }
                };

                // ==> layouts - Private Accessors



                // ==> layouts - Private Operators

                /** 
                    @throw rc<>
                */
                inline layout & operator[](layout_index p_index) ;


                // ==> layouts - Private Processors

                /** */
                void move_first_to_last();

                /** */
                inline void sort();

                /** */
                void unpositioned() const noexcept;

                /** */
                void unposition();

                // ==> layouts - Private Operators

                // ==> layouts - Private Attributes

                /** */
                collection m_collection;

            };
        }
    }
}
// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::interlace_text::core;


// -----------------------------------------------------------------------------
template<typename supplier>
const class_id
layouts<supplier>::
id;

// -----------------------------------------------------------------------------
template<typename supplier>
layouts<supplier>::
layouts() 
    : m_collection() {}
                
// -----------------------------------------------------------------------------
template<typename supplier>
layouts<supplier>::
layouts(const layouts & p_coords) 
    : m_collection(p_coords.m_collection) {}

// -----------------------------------------------------------------------------
template<typename supplier>
layouts<supplier>::
layouts(layouts && p_coords) {
    m_collection = std::move(p_coords.m_collection);
}

// -----------------------------------------------------------------------------
template<typename supplier>
layouts<supplier>::
~layouts() {}
                
// -----------------------------------------------------------------------------
template<typename supplier>
typename layouts<supplier>::layout_index
layouts<supplier>::
get_size() const {return m_collection.size();}
                
// -----------------------------------------------------------------------------
template<typename supplier>
bool
layouts<supplier>::
is_positioned() const noexcept {
    layout_index _last = m_collection.size();
    
    for (layout_index _i = 0; _i < _last; ++_i) {
        if (!m_collection[_i].is_positioned()) {
            return false;
        }
    }
    return true;
}

// -----------------------------------------------------------------------------
template<typename supplier>
void
layouts<supplier>::
sort() {
    compare_layout_by_text_size_descendant _cmp;
    std::sort(m_collection.begin(), m_collection.end(), _cmp);
}

// -----------------------------------------------------------------------------
template<typename supplier>
void
layouts<supplier>::
unpositioned() const noexcept {
    layout_index _size = m_collection.size();
    for (layout_index _i = 0; _i < _size; ++_i) {
        m_collection[_i].unposition();
    }
}

                
// -----------------------------------------------------------------------------
template<typename supplier>
layouts<supplier> & 
layouts<supplier>::
operator=(const layouts & p_coords) {
    if (this != & p_coords) {
        m_collection = p_coords.m_collection;
    }
    return *this;
}

                
// -----------------------------------------------------------------------------
template<typename supplier>
layouts<supplier> & 
layouts<supplier>::
operator=(layouts && p_coords) {
    if (this != & p_coords) {
        m_collection = std::move(p_coords.m_collection);
    }
    return *this;
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
bool 
layouts<supplier>::
operator ==(const layouts & p_coords) noexcept {
    layout_index _last = m_collection.size();
    layout_index _i = 0;
    for (; _i < _last; ++_i) {    
        if (m_collection[_i] != p_coords.m_collection[_i]) {
            return false;
        }        
    }
    return true;
}

                
// -----------------------------------------------------------------------------
template<typename supplier>
bool
layouts<supplier>::
operator !=(const layouts & p_coords) noexcept {
    layout_index _last = m_collection.size();
    layout_index _i = 0;
    
    for (; _i < _last; ++_i) {    
        if (m_collection[_i] != p_coords.m_collection[_i]) {
            return true;
        }        
    }
    return false;
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
typename layouts<supplier>::layout &
layouts<supplier>::
operator[](layout_index p_index) {

    if (p_index >= m_collection.size()) {
        throw rc<>(id, constants::index_overflow, __FILE__, __LINE__);
    }

    if (p_index < 0) {
        throw rc<>(id, constants::index_underflow, __FILE__, __LINE__);
    }

    return m_collection[p_index];
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
const typename layouts<supplier>::layout &
layouts<supplier>::
operator[](layout_index p_index) const  {

    if (p_index >= m_collection.size()) {
        throw rc<>(id, constants::index_overflow, __FILE__, __LINE__);
    }

    if (p_index < 0) {
        throw rc<>(id, constants::index_underflow, __FILE__, __LINE__);
    }

    return m_collection[p_index];
}



// -----------------------------------------------------------------------------
template<typename supplier>
void
layouts<supplier>::
add(const text & p_text) {
    compare_layout _cmp(p_text);
    auto _res = std::find_if(m_collection.begin(), m_collection.end(), _cmp);
    
    if ( _res == m_collection.end()) {
        m_collection.push_back(layout(static_cast<layout_id>(m_collection.size()),
                                      p_text)); 
    }
}

    
// -----------------------------------------------------------------------------
template<typename supplier>
template <typename t_out>
void
layouts<supplier>::
print(t_out & p_out) {
    layout_index _last_lay = m_collection.size() - 1;
    for (layout_index _i = 0; _i <= _last_lay; ++_i) {
        p_out << m_collection[_i] << std::endl;
    }
}

// -----------------------------------------------------------------------------
template<typename supplier>
typename layouts<supplier>::layout_index 
layouts<supplier>::
get_last() {return m_collection.size() - 1;}

// -----------------------------------------------------------------------------
template<typename supplier>
void
layouts<supplier>::
move_first_to_last() {
    
    layout _layout = std::move(m_collection[0]);

    m_collection.erase(m_collection.begin());
        
    m_collection.push_back(std::move(_layout));
}

// -----------------------------------------------------------------------------
template<typename supplier>
typename layouts<supplier>::coordinate
layouts<supplier>::
greatest() const {
    // find the most negative x and y coordinate
    position _line = 0;
    position _col = 0;

    layout_index _size_layouts = get_size();
    for (layout_index _lay_index = 0; _lay_index < _size_layouts; ++_lay_index) {
        const coordinates & _coords = m_collection[_lay_index].get_coordinates();
        coordinate_index _size_coords = _coords.get_size();
        for (coordinate_index _coord_index = 0;
             _coord_index < _size_coords;
             ++_coord_index) {
            if (_coords[_coord_index].get_line() > _line) {
                _line = _coords[_coord_index].get_line();
            }
            if (_coords[_coord_index].get_column() > _col) {
                _col = _coords[_coord_index].get_column();
            }
        }
    }
    return coordinate(_line, _col, ' ');
}



#endif

